package com.statistics.word.impl.excel;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.Map;

/**
 *  word 解析助手
 * @author HeWei123
 */
@Slf4j
public final class WordHelper {

    /**
     * Map 存储都是字符串跟具体的类型不匹配会反射失败
     * 获取字段类型 除字符串外  手动替换成  该字段正确的数据类型
     *
     * @param genericType 实体类字段类型
     * @param map         存储的集合
     * @param next1       存储集合的key
     */
    public static Map<String, Object> replaceClass(Type genericType, Map<String, Object> map, String next1) {
        if (!genericType.getTypeName().equals("java.lang.String")) {
            if (genericType.getTypeName().equals("java.math.BigDecimal")) {
                if ("".equals(map.get(next1).toString())) {
                    map.put(next1, null);
                }else {
                    BigDecimal bigDecimal = new BigDecimal(map.get(next1).toString());
                    map.put(next1, bigDecimal);}
            } else if (genericType.getTypeName().equals("java.time.LocalDateTime")) {
                LocalDateTime ldt = null;
                try {

                    ldt = LocalDateTime.parse(map.get(next1).toString());
                } catch (DateTimeParseException exception) {
                    if (!map.isEmpty() && !"".equals(map.get(next1))) {
                        LocalDate parse = LocalDate.parse(map.get(next1).toString());
                        ZoneId zone = ZoneId.systemDefault();
                        Instant instant = parse.atStartOfDay().atZone(zone).toInstant();
                        ldt = LocalDateTime.ofInstant(instant, zone);
                    }

                } finally {
                    map.put(next1, ldt);
                }
            } else if (genericType.getTypeName().equals("java.time.LocalDate")) {
                LocalDate ldt = null;
                try {
                    ldt = LocalDate.parse(map.get(next1).toString());

                } catch (DateTimeParseException exception) {
                    if ("".equals(map.get(next1).toString())){
                        ldt=null;
                    }else {
                        LocalDateTime parse = LocalDateTime.parse(map.get(next1).toString());
                        ldt = parse.toLocalDate();
                    }
                } finally {
                    map.put(next1, ldt);
                }

            }
        }
        return map;
    }

    /**
     * 获取泛型对象
     *
     * @param clazz
     * @return
     */
    public static Class<?> currentModelClass(Class<?> clazz) {
        // 获取超类
        Type type = clazz.getGenericSuperclass();
        if (!(type instanceof ParameterizedType)) {
            throw new RuntimeException("超类没有参数化类型");
        }
        // 获取超累泛型上设置的类
        Type trueType = ((ParameterizedType) type).getActualTypeArguments()[0];
        if (!(trueType instanceof Class)) {
            throw new RuntimeException("没有在超累泛型上设置实际的类");
        }

        return (Class<?>) trueType;
    }


    /**
     * 根据CellType判断解析的值为字符串还是数值
     * 如是字符串：
     * 根据 list 和  单元小标获得当列的表头作为key 存在字符串
     * 如是数值类型 ：
     * 判断是否时间类型：
     * 如是时间类型判断时间格式：date 还是 datetime 转换成对应的 LocalDate 和 LocalDateTime 存储
     * 判断是否整数
     * 如是整数 格式化数据 （poi会自动把大整数给计算）存储
     *
     * @param cell cell对象
     * @param map  存储解析出来的行级数据
     * @param list 存储表头的集合，用于赋值存储行级数据Map的key
     * @param i    一行的单元格数的下标
     * @return
     */
    public static Map<String, Object> addMapDate(Cell cell, Map<String, Object> map, List<String> list, int i) {

        switch (cell.getCellType()) {
            /**
             * 字符串
             */
            case STRING:
                map.put(list.get(i), cell.getRichStringCellValue().getString());
                break;
            /**
             * 整形 小数 日期
             */
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    String dataFormatString = cell.getCellStyle().getDataFormatString();
                    Instant dateCellValue = cell.getDateCellValue().toInstant();
                    ZoneId zoneId = ZoneId.systemDefault();
                    if (dataFormatString.equals("m/d/yy h:mm")) {
                        LocalDateTime localDateTime = dateCellValue.atZone(zoneId).toLocalDateTime();
                        map.put(list.get(i), localDateTime);
                    } else if (dataFormatString.equals("m/d/yy") || dataFormatString.equals("yyyy\\-m\\-d") ) {
                        LocalDate localDateTime = dateCellValue.atZone(zoneId).toLocalDate();
                        map.put(list.get(i), localDateTime);
                    } else if (dataFormatString.equals("yyyy/m/d\\ h:mm:ss")
                            || dataFormatString.equals("yyyy/m/d\\ hh:mm:ss")
                            || dataFormatString.equals("yyyy\\-m\\-d\\ h:mm:ss")) {
                        LocalDateTime localDateTime = dateCellValue.atZone(zoneId).toLocalDateTime();
                        map.put(list.get(i), localDateTime);
                    }
                } else if (isNumeric(new DecimalFormat("#.00").format(cell.getNumericCellValue()))) {
                    map.put(list.get(i), new DecimalFormat("#.00").format(cell.getNumericCellValue()));
                } else {

                    // #：没有则为空
                    DecimalFormat df = new DecimalFormat("#.###");
                    String format = df.format(cell.getNumericCellValue());
                    map.put(list.get(i), format);
                }
                break;
            default:
                break;
        }

        return map;
    }

    private static boolean isNumeric(final String str) {
        // null or empty
        if (str == null || str.length() == 0) {
            return false;
        }
        return str.chars().allMatch(Character::isDigit);

    }
}
